package com.chushouya.common.util;

import com.chushouya.common.dto.QueueMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * RabbitMQ队列工具类
 * 提供简单易用的消息发送和延时队列功能
 * 
 * @author chushouya team
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "spring.rabbitmq.enabled", havingValue = "true", matchIfMissing = true)
public class RabbitMQUtil {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private AmqpAdmin amqpAdmin;

    /**
     * 发送普通消息
     * 
     * @param queueName 队列名称
     * @param message 消息内容
     */
    public void sendMessage(String queueName, Object message) {
        try {
            ensureQueueExists(queueName);
            QueueMessage<Object> queueMessage = new QueueMessage<>(message);
            rabbitTemplate.convertAndSend(queueName, queueMessage);
            log.info("发送消息成功 - 队列: {}, 消息ID: {}", queueName, queueMessage.getMessageId());
        } catch (Exception e) {
            log.error("发送消息失败 - 队列: {}, 错误: {}", queueName, e.getMessage(), e);
            throw new RuntimeException("发送消息失败", e);
        }
    }

    /**
     * 发送带类型的消息
     * 
     * @param queueName 队列名称
     * @param messageType 消息类型
     * @param message 消息内容
     */
    public void sendMessage(String queueName, String messageType, Object message) {
        try {
            ensureQueueExists(queueName);
            QueueMessage<Object> queueMessage = new QueueMessage<>(messageType, message);
            rabbitTemplate.convertAndSend(queueName, queueMessage);
            log.info("发送消息成功 - 队列: {}, 类型: {}, 消息ID: {}", queueName, messageType, queueMessage.getMessageId());
        } catch (Exception e) {
            log.error("发送消息失败 - 队列: {}, 类型: {}, 错误: {}", queueName, messageType, e.getMessage(), e);
            throw new RuntimeException("发送消息失败", e);
        }
    }

    /**
     * 发送延时消息（毫秒）
     * 
     * @param queueName 目标队列名称
     * @param message 消息内容
     * @param delayMillis 延时毫秒数
     */
    public void sendDelayMessage(String queueName, Object message, long delayMillis) {
        try {
            ensureQueueExists(queueName);
            String delayQueueName = queueName + ".delay";
            ensureDelayQueueExists(delayQueueName, queueName, delayMillis);
            
            QueueMessage<Object> queueMessage = new QueueMessage<>(message);
            queueMessage.setDelayTime(delayMillis);
            
            rabbitTemplate.convertAndSend(delayQueueName, queueMessage);
            log.info("发送延时消息成功 - 目标队列: {}, 延时: {}ms, 消息ID: {}", queueName, delayMillis, queueMessage.getMessageId());
        } catch (Exception e) {
            log.error("发送延时消息失败 - 队列: {}, 延时: {}ms, 错误: {}", queueName, delayMillis, e.getMessage(), e);
            throw new RuntimeException("发送延时消息失败", e);
        }
    }

    /**
     * 发送延时消息（时间单位）
     * 
     * @param queueName 目标队列名称
     * @param message 消息内容
     * @param delay 延时时间
     * @param timeUnit 时间单位
     */
    public void sendDelayMessage(String queueName, Object message, long delay, TimeUnit timeUnit) {
        long delayMillis = timeUnit.toMillis(delay);
        sendDelayMessage(queueName, message, delayMillis);
    }

    /**
     * 发送带类型的延时消息
     * 
     * @param queueName 目标队列名称
     * @param messageType 消息类型
     * @param message 消息内容
     * @param delayMillis 延时毫秒数
     */
    public void sendDelayMessage(String queueName, String messageType, Object message, long delayMillis) {
        try {
            ensureQueueExists(queueName);
            String delayQueueName = queueName + ".delay";
            ensureDelayQueueExists(delayQueueName, queueName, delayMillis);
            
            QueueMessage<Object> queueMessage = new QueueMessage<>(messageType, message);
            queueMessage.setDelayTime(delayMillis);
            
            rabbitTemplate.convertAndSend(delayQueueName, queueMessage);
            log.info("发送延时消息成功 - 目标队列: {}, 类型: {}, 延时: {}ms, 消息ID: {}", queueName, messageType, delayMillis, queueMessage.getMessageId());
        } catch (Exception e) {
            log.error("发送延时消息失败 - 队列: {}, 类型: {}, 延时: {}ms, 错误: {}", queueName, messageType, delayMillis, e.getMessage(), e);
            throw new RuntimeException("发送延时消息失败", e);
        }
    }

    /**
     * 发送带类型的延时消息（时间单位）
     * 
     * @param queueName 目标队列名称
     * @param messageType 消息类型
     * @param message 消息内容
     * @param delay 延时时间
     * @param timeUnit 时间单位
     */
    public void sendDelayMessage(String queueName, String messageType, Object message, long delay, TimeUnit timeUnit) {
        long delayMillis = timeUnit.toMillis(delay);
        sendDelayMessage(queueName, messageType, message, delayMillis);
    }

    /**
     * 发送到指定交换机和路由键
     * 
     * @param exchange 交换机名称
     * @param routingKey 路由键
     * @param message 消息内容
     */
    public void sendToExchange(String exchange, String routingKey, Object message) {
        try {
            QueueMessage<Object> queueMessage = new QueueMessage<>(message);
            rabbitTemplate.convertAndSend(exchange, routingKey, queueMessage);
            log.info("发送消息到交换机成功 - 交换机: {}, 路由键: {}, 消息ID: {}", exchange, routingKey, queueMessage.getMessageId());
        } catch (Exception e) {
            log.error("发送消息到交换机失败 - 交换机: {}, 路由键: {}, 错误: {}", exchange, routingKey, e.getMessage(), e);
            throw new RuntimeException("发送消息到交换机失败", e);
        }
    }

    /**
     * 确保队列存在，不存在则创建
     * 
     * @param queueName 队列名称
     */
    private void ensureQueueExists(String queueName) {
        try {
            Queue queue = QueueBuilder.durable(queueName).build();
            amqpAdmin.declareQueue(queue);
        } catch (Exception e) {
            log.warn("创建队列失败或队列已存在: {}", queueName);
        }
    }

    /**
     * 确保延时队列存在，不存在则创建
     * 
     * @param delayQueueName 延时队列名称
     * @param targetQueueName 目标队列名称
     * @param ttl TTL时间（毫秒）
     */
    private void ensureDelayQueueExists(String delayQueueName, String targetQueueName, long ttl) {
        try {
            // 先检查队列是否已存在
            Properties queueProperties = amqpAdmin.getQueueProperties(delayQueueName);
            if (queueProperties != null) {
                // 队列已存在，不需要重新创建
                log.debug("延时队列已存在: {}", delayQueueName);
                return;
            }
            
            // 创建延时队列，设置TTL和死信交换机
            Map<String, Object> args = new HashMap<>();
            args.put("x-message-ttl", ttl);
            args.put("x-dead-letter-exchange", "");
            args.put("x-dead-letter-routing-key", targetQueueName);
            
            Queue delayQueue = new Queue(delayQueueName, true, false, false, args);
            amqpAdmin.declareQueue(delayQueue);
            log.info("创建延时队列成功: {}, TTL: {}ms", delayQueueName, ttl);
        } catch (Exception e) {
            log.warn("创建延时队列失败或队列已存在: {}, 错误: {}", delayQueueName, e.getMessage());
        }
    }

    /**
     * 获取队列消息数量
     * 
     * @param queueName 队列名称
     * @return 消息数量
     */
    public long getQueueMessageCount(String queueName) {
        try {
            Properties properties = amqpAdmin.getQueueProperties(queueName);
            if (properties != null) {
                return (Integer) properties.get("QUEUE_MESSAGE_COUNT");
            }
            return 0;
        } catch (Exception e) {
            log.error("获取队列消息数量失败: {}", queueName, e);
            return 0;
        }
    }

    /**
     * 删除队列
     * 
     * @param queueName 队列名称
     * @return 是否删除成功
     */
    public boolean deleteQueue(String queueName) {
        try {
            return amqpAdmin.deleteQueue(queueName);
        } catch (Exception e) {
            log.error("删除队列失败: {}", queueName, e);
            return false;
        }
    }

    /**
     * 清空队列
     * 
     * @param queueName 队列名称
     */
    public void purgeQueue(String queueName) {
        try {
            amqpAdmin.purgeQueue(queueName);
            log.info("清空队列成功: {}", queueName);
        } catch (Exception e) {
            log.error("清空队列失败: {}", queueName, e);
            throw new RuntimeException("清空队列失败", e);
        }
    }
}
